﻿using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/* TrackGroup objects contain Track objects that are connected to each other in a single unified piece */
// funtionality to be added in next step: adding neighbor chains, destroying neighbor chains
// step + 1 functionality: remove tracks, see if it causes a continuity problem, splitting group into multiple groups

// TrackGroups are entirely just DataMembers and so do not need to have a Start method, I hope!
class TrackGroup
{
    /* Private variable declarations */
    private static int S_ID = 0;
    
	private int _ID;
	public int ID
	{
		get
		{
			return _ID;
		}
	}

    private HashSet<Track> T_List; // changed to HashSet<Track> for search speed
	
    public TrackGroup()
    {
        _ID = S_ID++;
        T_List = new HashSet<Track>(); // changed to HashSet<Track> to match var declaration
		
        //Console.Write("\tNew TrackGroup["+m_ID+"] added!\n");
    }
	
    public TrackGroup(Track firstMember)
    {
        _ID = S_ID++;
        T_List = new HashSet<Track>(); // changed to HashSet<Track> to match var declaration

        //Console.Write("\tNew TrackGroup["+m_ID+"] added!\n");
        this.addTrack(firstMember);
    }
	
    public TrackGroup(HashSet<Track> allMembers) // changed from List<Track> to HashSet<Track> to match T_List typing
    {
        _ID = S_ID++;
        T_List = allMembers;
    }
	
    public int getNumMembers()
    {
        try
        {
            return T_List.Count(); // attempt to return the count
        }
        catch (System.ArgumentNullException) // if the count = 0 or T_List == null then return 0
        {
            return 0;
        }
    }
	
    public void removeTrack(Track t)
    {
        Console.WriteLine("Attempting to remove Track[{0}] from TrackGroup[{1}]", t.ID, this._ID);
        T_List.Remove(t); // will remove the Track (t) IFF it is available for removal.

        #region SUPERFLOUS CODE!
        /*
        if (this.contains(t))
        {
            
            
            
                Console.WriteLine("\tRemoval of Track[{0}] from TrackGroup[{1}] failed", t.ID, this._ID);
                Console.Write("\tTrying again but by index...");
                // get index of t
                int index_t = T_List.IndexOf(t);
                Console.Write("Track[{0}]...", index_t);
                if (index_t >= 0 && index_t < T_List.Count())
                {
                    T_List.RemoveAt(index_t);
                }

                if (this.contains(t))
                {
                    Console.Write("Failed");
                }
                else
                {
                    Console.Write("Success");
                }
            
            

                Console.WriteLine();
        }
        else
        {
            Console.WriteLine("\tRemoval of Track[{0}] from TrackGroup[{1}] succeeded", t.ID, this.ID);
        }
        */
        #endregion
    }
	
    public void addTrack(Track t)
    {
        // make sure that track t is not null
        if (t != null)
        {
            T_List.Add(t);
        }
    }
	
    //TODO alter this method to take advantage of the TrackConstants values and search UP TO 2x Length and 1.5x Width for a "Raycasting Box" to see if the Track could be close enough to collide with another TrackPiece along its rotational vector from its coordinate vector
    public Track getClosestMember(Track testTrack, float maxDistance)
    {
        if (getNumMembers() > 0)
        {
            float distance = 0.0f, pDistance = 0.0f;
            int firstID = T_List.First().ID;
            int bestID = firstID;
            Track retTrack = null;

            Vector3 t_coord = testTrack.coord, m_coord;
            foreach (Track t in T_List)
            {
                m_coord = t.coord;

                if (t.ID == firstID)
                {
                    pDistance = Vector3.Distance(t_coord, m_coord);
                    retTrack = t; // here just in case the firstID is also the lastID
                }
                else
                {
                    distance = Vector3.Distance(t_coord, m_coord);
                    if (pDistance > distance)
                    {
                        pDistance = distance;
                        bestID = t.ID;
                        retTrack = t;
                    }
                }
            }
            //Console.WriteLine("Max Distance = {0} ; Best Distance = {1}; Best ID = {2}", maxDistance, pDistance,bestID);
            //Console.WriteLine("Best Distance <= Max Distance = {0}", pDistance <= maxDistance);
            if (pDistance <= maxDistance)
            {
                //Console.WriteLine("returnTrack.ID = {0}", retTrack.ID);
                return retTrack;
            }
            else
            {
                return null;
            }
        }
        else
        {
            return null;
        }
    }
	
    public bool contains(Track track)
    {
        return T_List.Contains(track);
        #region SUPERFLOUS with change from List<T> to HashSet<T> for the T_List variable
        /*
        foreach (Track T in T_List)
        {
            if (T.isEqual(track))
            {
                return true;
            }
        }
        return false;
        //*/
        #endregion
    }
	
    public bool contains(int trackID) // looks for trackID amongst its lists
    {
        // not sure of a quick way around this one other than just getting rid of it once IDs are phased out
        foreach (Track T in T_List)
        {
            if (T.ID == trackID)
            {
                return true;
            }
        }
        return false;
    }
	
	
	// shouldn't even need this anymore, as long as we know that the Track is in here then we can just grab it
    public Track getTrackByID(int trackID)
    {
        foreach (Track T in T_List){
            if (T.ID == trackID)
            {
                return T;
            }
        }
        return null; // should never happen if contains(trackID) is done before this one
    }

    public void printMembers()
    {
        foreach (Track T in T_List)
        {
            Console.Write("\tTrack[{0}] has {1} Neighbors\n",T.ID,T.getNumNeighbors());
            T.printNeighbors();
        }
    }

    public HashSet<Track> getAllTracks() // Changed RetVal from List<Track> to HashSet<Track>
    {
        return T_List;
    }

    // TODO research fast HashSet merging
    public void merge(HashSet<Track> newItems) // Changed param-type of newItems from List<Track> to HashSet<Track> since they will be passed in as a HashSet instead of a List now
    {// there might be a faster way to merge hashsets but I am not sure of it at the moment. Research is needed
        foreach (Track T in newItems){
            this.addTrack(T);
        }
    }
	
    public void clearList()
    {
        T_List.Clear();
    }
}